package hibernate.model;

import lombok.Data;
import lombok.NoArgsConstructor;

import javax.persistence.*;
import java.util.Date;
import java.util.Set;

@NamedQuery(name = "testNamedQuery", query = "FROM JPA_CUTOMERS c WHERE c.id = ?0")
@Cacheable
@Data
@NoArgsConstructor
/**
 * @Entity 该Java类为实体类，将映射到数据库表。如声明一个实体类 Customer，它将映射到数据库中的 customer 表上。
 */
@Entity(name = "JPA_CUTOMERS")
/**
 * @Table 当实体类与其映射的数据库表名不同名时使用
 *  name，用于指明数据库的表名
 *  catalog 和 schema 用于设置表所属的数据库目录或模式，通常为数据库名
 *  uniqueConstraints 选项用于设置约束条件，通常不须设置
 */
@Table(name = "JPA_CUTOMERS")
public class Customer {
    @Id // 声明一个实体类的属性映射为数据库的主键列。标注可写在属性的 getter 方法上。
    // @GeneratedValue 用于标注主键的生成策略，通过 strategy 属性指定
    // 默认 JPA 自动选择一个最适合底层数据库的主键生成策略：SqlServer 对应 identity，MySQL 对应 auto increment
    // 在 javax.persistence.GenerationType 中定义了以下几种可供选择的策略：
    // IDENTITY：采用数据库 ID自增长的方式来自增主键字段，Oracle 不支持这种方式
    // AUTO： JPA自动选择合适的策略，默认选项
    // SEQUENCE：通过序列产生主键，通过 @SequenceGenerator 注解指定序列名，MySql 不支持这种方式
    // TABLE：通过表产生主键，框架借由表模拟序列产生主键，使用该策略可以使应用更易于数据库移植
    @GeneratedValue(strategy = GenerationType.AUTO)
    // @TableGenerator 将当前主键的值保存到一个数据库表中，主键的值每次都从指定的表中查询获得
    // 这种主键策略可以适用于任何数据库，不必担心不同数据库不兼容造成的问题
    // name 该主键生成策略的名称，它与 @GeneratedValue 中设置的 generator 值对应
    // table 表生成策略所持久化的表名
    // pkColumnName 在持久化表中，该主键生成策略所对应键值的名称
    // valueColumnName 在持久化表中，该主键当前所生成的值，它的值将会随着每次创建累加
    // pkColumnValue 在持久化表中，该生成策略所对应的主键
    // allocationSize 每次主键值增加的大小, 默认值为 50
    // @TableGenerator(name = "xxx", table = "jpa_id", pkColumnName = "pk_name", pkColumnValue = "CUTOMERS_ID",
    //         valueColumnName = "pk_value", allocationSize = 2)
    Integer id;

    /**
     * @Column 当实体类属性与其映射的数据库表的列不同名时使用
     * name，用于设置映射数据库表的列名。此外，该标注还包含其它多个属性，如：unique 、nullable、length 等
     * columnDefinition 属性: 表示该字段在数据库中的实际类型
     * 通常 ORM 框架可以根据属性类型自动判断数据库中字段的类型,但是对于Date类型仍无法确定数据库中字段类型究竟是 DATE,TIME 还是 TIMESTAMP
     * 此外 String 的默认映射类型为 VARCHAR, 如果要将 String 类型映射到特定数据库的 BLOB 或 TEXT 字段类型
     */
    @Column(name = "LAST_NAME", length = 50, nullable = false)
    String lastName;

    /**
     * @Basic 表示属性到数据库表字段的映射, 对于没有任何注解的 getXxxx() 方法,默认为写了 @Basic
     * fetch: 表示该属性的读取策略,有 EAGER 和 LAZY 两种,分别表示主支抓取和延迟加载,默认为 EAGER.
     * optional:表示该属性是否允许为null, 默认为true
     */
    @Basic
    String email;

    int age;

    /**
     * @Temporal 在进行时间类属性映射时调整精度
     * 在核心的 Java API 中没有定义 Date 类型的精度(temporal precision)
     * 在数据库中，表示 Date 类型的数据有 DATE，TIME 和 TIMESTAMP 三种精度(即单纯的日期，时间，或者两者兼备)
     */
    @Temporal(TemporalType.TIMESTAMP)
    Date birth;

    @Temporal(TemporalType.DATE)
    Date createdTime;

    /**
     * @Transient 表示该属性不需要映射到数据库表字段，ORM 框架忽略该属性
     * 如果一个属性并非数据库表字段映射,需写上 @Transient，否则 ORM 框架默认其注解为 @Basic
     */
    @Transient
    String info;

    // 使用 @OneToMany 来映射 1-n 的关联关系//注意: 若在 1 的一端的 @OneToMany 中使用 mappedBy 属性, 则 @OneToMany 端就不能再使用 @JoinColumn 属性了
    @OneToMany(mappedBy = "customer")
    Set<Order> orders;

    public Customer(String lastName) {
        this.lastName = lastName;
    }
}